Skip to main content
Version: 1.0.0

Introduction to Point Layer

What is a point layer?

If measures are assigned to both of the axes, Muze renders a layer with point mark. Each point in the following chart can take any continuous value in x and y coordinate. It forms the basis of scatter and bubble charts.

Example

const { muze, getDataFromSearchQuery } = viz;

const data = getDataFromSearchQuery();

muze
  .canvas()
  .rows(["Displacement"])
  .columns(["Horsepower"])
  .detail(["Name"])
  .data(data)
  .mount("#chart");

NOTE: You can apply color, shape and size retinal encodings on point layer.

Creating a scatter plot

Mapping two measure(quantitative) fields with a dimension as detail field produces a scatter plot.

Example

const { muze, getDataFromSearchQuery } = viz;

const data = getDataFromSearchQuery();

muze
  .canvas()
  .rows(["Acceleration"])
  .columns(["Horsepower"])
  .detail(["Name"])
  .data(data)
  .mount("#chart");

Creating a scatter plot matrix

Adding on to the previous scatter plot, we plot 3 fields both on rows and columns and color the chart by Cylinders field to get a scatter plot matrix.

Example

const { muze, getDataFromSearchQuery } = viz;

const data = getDataFromSearchQuery();

muze
  .canvas()
  .rows(["Acceleration", "Horsepower", "Miles_per_Gallon"])
  .columns(["Miles_per_Gallon", "Horsepower", "Acceleration"])
  .detail(["Name"])
  .color("Cylinders")
  .data(data)
  .mount("#chart");

Creating a dotplot chart

Below is a dot-plot for acceleration over the years colored by Origin.

Example

const { muze, getDataFromSearchQuery } = viz;

const data = getDataFromSearchQuery();

muze
  .canvas()
  .rows(["Acceleration"])
  .columns(["Year"])
  .color("Origin")
  .detail(["Name"])
  .layers([
    {
      mark: "point",
    },
  ])
  .config({
    interaction: {
      behaviours: {
        highlight: {
          exact: true,
        },
      },
    },
  })
  .data(data)
  .mount("#chart");

Creating a scatter plot with custom shape

Example

const { muze, getDataFromSearchQuery } = viz;

const data = getDataFromSearchQuery();

let dm = new DataModel(data);
const html = muze.Operators.html;
const smileys = {
  smiley1:
    '<g class="smiley smiley-0" fill="#fffae6" transform="translate(-26,-26)"><circle r="22" cx="26" cy="26"></circle><g class="smiley-expression" transform="translate(4,4) scale(1.4666666666666666)"><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -7.673427e-02 7.712884e-02)" class="st0" cx="15" cy="15" rx="15" ry="15"></ellipse><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -5.115256e-02 0.1111)" class="st1" cx="21.6" cy="10" rx="1.1" ry="1.1"></ellipse><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -5.167323e-02 4.345998e-02)" class="st1" cx="8.4" cy="10.1" rx="1.1" ry="1.1"></ellipse><line class="st2" x1="20" y1="22.2" x2="11.5" y2="22.2"></line></g></g>',
  smiley2:
    '<g class="smiley smiley-1" fill="#ffefab" transform="translate(-26,-26)"><circle r="22" cx="26" cy="26"></circle><g class="smiley-expression" transform="translate(4,4) scale(1.4666666666666666)"><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -7.673427e-02 7.712884e-02)" class="st5" cx="15" cy="15" rx="15" ry="15"></ellipse><path class="st2" d="M6.8,17c2.5,4.5,8.3,6.1,12.8,3.6c1.3-0.7,2.4-1.7,3.2-3"></path><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -5.115328e-02 0.1109)" class="st1" cx="21.6" cy="10" rx="1.1" ry="1.1"></ellipse><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -5.167395e-02 4.317867e-02)" class="st1" cx="8.4" cy="10.1" rx="1.1" ry="1.1"></ellipse></g></g>',
  smiley3:
    '<g class="smiley smiley-2" fill="#ffe473" transform="translate(-26,-26)"><circle r="22" cx="26" cy="26"></circle><g class="smiley-expression" transform="translate(4,4) scale(1.4666666666666666)"><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -7.673427e-02 7.712884e-02)" class="st5" cx="15" cy="15" rx="15" ry="15"></ellipse><path class="st2" d="M6.8,17c2.5,4.5,8.3,6.1,12.8,3.6c1.3-0.7,2.4-1.7,3.2-3"></path><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -5.115328e-02 0.1109)" class="st1" cx="21.6" cy="10" rx="1.1" ry="1.1"></ellipse><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -5.167395e-02 4.317867e-02)" class="st1" cx="8.4" cy="10.1" rx="1.1" ry="1.1"></ellipse></g></g>',
  smiley4:
    '<g class="smiley smiley-3" fill="#ffcc00" transform="translate(-26,-26)"><circle r="22" cx="26" cy="26"></circle><g class="smiley-expression" transform="translate(4,4) scale(1.4666666666666666)"><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -7.673643e-02 7.684924e-02)" class="st6" cx="14.9" cy="15" rx="15" ry="15"></ellipse><path class="st7" d="M5.6,15.1c0,5.2,4.2,9.4,9.4,9.3c5.2,0,9.4-4.2,9.3-9.4"></path><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -5.115504e-02 0.1109)" class="st1" cx="21.6" cy="10" rx="1.1" ry="1.1"></ellipse><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -5.167570e-02 4.318081e-02)" class="st1" cx="8.4" cy="10.1" rx="1.1" ry="1.1"></ellipse></g></g>',
  smiley5:
    '<g class="smiley smiley-4" fill="#eabe03" transform="translate(-26,-26)"><circle r="22" cx="26" cy="26"></circle><g class="smiley-expression" transform="translate(4,4) scale(1.4666666666666666)"><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -7.673427e-02 7.712884e-02)" class="st6" cx="15" cy="15" rx="15" ry="15"></ellipse><path class="st2" d="M5.6,15.1c0,5.2,4.2,9.4,9.4,9.3c5.2,0,9.4-4.2,9.3-9.4L5.6,15.1z"></path><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -5.115260e-02 0.1109)" class="st1" cx="21.6" cy="10" rx="1.1" ry="1.1"></ellipse><ellipse transform="matrix(1 -5.128771e-03 5.128771e-03 1 -5.167491e-02 4.317867e-02)" class="st1" cx="8.4" cy="10.1" rx="1.1" ry="1.1"></ellipse></g></g>',
};

const stateGrid = [
  [null, null, null, null, null, "WI", null, null, null, "VT", "ME"],
  ["WA", "ID", "MT", "ND", "MN", "IL", "MI", null, "NY", "MA", "NH"],
  ["OR", "NV", "WY", "SD", "IN", "IA", "OH", "PA", "NJ", "CT", "RI"],
  ["CA", "UT", "CO", "NE", "MO", "KY", "WV", "VA", "MD", "DE", null],
  [null, "AZ", "NM", "KS", "AR", "TN", "NC", "SC", null, null, null],
  [null, null, null, "OK", "LA", "MS", "AL", "GA", null, null, null],
  ["HI", "AK", null, "TX", null, null, null, null, "FL", null, null],
];
const alphabets = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"];

dm = dm.calculateVariable(
  {
    name: "Latiudinal-Dimension",
    type: "dimension",
  },
  ["State Codes"],
  (state) => {
    let x = -1;
    stateGrid.forEach((e, i) => {
      if (e.indexOf(state) > -1) {
        x = `${i}`;
      }
    });
    return alphabets[x];
  }
);

dm = dm.calculateVariable(
  {
    name: "Longitudinal-Dimension",
    type: "dimension",
  },
  ["State Codes"],
  (state) => {
    let x = -1;
    stateGrid.forEach((e) => {
      if (e.indexOf(state) > -1) {
        x = `${e.indexOf(state)}`;
      }
    });
    return alphabets[x];
  }
);

dm = dm.sort([["Well-being Index"]]);

let wellBeingArray = dm.getField("Well-being Index").data();
const quantiles = Math.round(wellBeingArray.length / 5);

dm = dm.calculateVariable(
  {
    name: "Well-being-quantile",
    type: "dimension",
  },
  ["Well-being Index"],
  (wbIndex) => {
    const index = wellBeingArray.indexOf(wbIndex);
    return Math.floor(index / quantiles);
  }
);

dm = dm.sort([
  ["Longitudinal-Dimension", "asc"],
  ["Latiudinal-Dimension", "asc"],
]);

muze
  .canvas()
  .rows(["Latiudinal-Dimension"])
  .columns(["Longitudinal-Dimension"])
  .shape("Well-being-quantile")
  .detail(["State Codes", "State", "Well-being Index"])
  .layers([
    {
      mark: "point",
      encoding: {
        color: {
          value: () => null,
        },
      },
      interaction: {
        focus: {
          style: {
            fill: null,
          },
        },
      },
    },
    {
      mark: "text",
      encoding: {
        text: "State Codes",
        color: {
          value: () => "rgba(0,0,0,.4)",
        },
      },
      encodingTransform: (points) => {
        points.forEach((e) => {
          e.update.y += 30;
        });
        return points;
      },
    },
  ])
  .config({
    gridLines: {
      y: {
        show: true,
      },
    },
    border: {
      showValueBorders: {
        left: false,
        bottom: false,
      },
    },
    axes: {
      y: {
        domain: ["a", "b", "c", "d", "e", "f", "g"].reverse(),
        show: false,
      },
      x: {
        show: false,
      },
    },
    gridLines: {
      y: {
        show: false,
      },
    },
    interaction: {
      tooltip: {
        formatter: (dataInfo) => {
          const { dataModel } = dataInfo;
          const tooltipData = dataModel.getData().data;
          // const fieldConfig = dataModel.getFieldsConfig();

          let tooltipContent = "";
          tooltipData.forEach((dataArray) => {
            const state = dataArray[dataModel.getFieldIndex("State")];
            const wellBeing =
              dataArray[dataModel.getFieldIndex("Well-being Index")];
            const quant =
              dataArray[dataModel.getFieldIndex("Well-being-quantile")];
            const wellBeingTypes = [
              "Poor",
              "Less Than Average",
              "Average",
              "Above Average",
              "Excellent",
            ];

            tooltipContent += `<p>The state of <b>${state}</b> has a well being score of <b>${wellBeing}</b>
                                        providing a <b>${wellBeingTypes[quant]}</b> quality of life</p>`;
          });
          return html`${tooltipContent}`;
        },
      },
    },
    legend: {
      position: "bottom",
      shape: {
        domain: ["0", "1", "2", "3", "4"],
        generator: (val) => {
          const pathVar = document.createElementNS(
            "http://www.w3.org/2000/svg",
            "g"
          );
          pathVar.innerHTML = smileys[`smiley${+val + 1}`];
          pathVar.setAttribute("class", "smiley-icon");
          pathVar.setAttribute("stroke", "black");
          pathVar.setAttribute("stroke-width", "1");
          return pathVar;
        },
        title: { text: "Well Being Score" },
        interaction: {
          highlight: {
            target: {
              layer: {
                highlight: {
                  sideEffects: {
                    "plot-highlighter": {
                      enabled: false,
                    },
                  },
                },
              },
            },
            sideEffects: {
              "legend-highlighter": {
                rules: [
                  {
                    target: "entrySet",
                    className: "muze-legend-brighten",
                  },
                ],
              },
            },
          },
        },
        item: {
          icon: {
            height: 40,
            width: 40,
          },
          text: {
            formatter: () => "",
            orientation: "bottom",
          },
        },
      },
    },
  })
  .title("Where to find the good life in USA?", { align: "center" })
  .subtitle("An example of Custom Shapes provided in Shape Encoding", {
    align: "center",
  })
  .data(dm)
  .width(830)
  .height(650)
  .mount("#chart");

The sample above may seem intimidating, so let's quickly run over it:

  • First, we add an object named smileys with 5 distinct svg groups with the shape we want
  • Next, we calculate two fields Latiudinal-Dimension and Longitudinal-Dimension which just assigns an alphabet to each row in the data
  • We then go on creating the chart with a custom shape using the shape generator api
  • We remove border, axes, gridlines and also place the legend on bottom
  • The tooltip is also customized using the tooltip's formatter function